﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Google.GData.Photos;
using Google.GData.Client;
using Google.GData.Extensions.MediaRss;
using Google.GData.Extensions;
using Google.Picasa;

namespace BloggerImageConversion
{
    public partial class MigrationForm : Form
    {
        private string user;
        private PicasaService picasaService;
        private PicasaFeed feed;
        private PicasaFeed photoFeed;
        private List<UserState> states = new List<UserState>();
        BindingList<ImageMigrationItem> imageMigrationCollection;
        private bool inProcess;

        private delegate void SaveAnotherPictureDelegate(UserState us);

        private bool InProcess
        {
            get
            {
                return inProcess;
            }
            set
            {
                inProcess = value;
                btnMigrate.Enabled = !inProcess;
            }
        }

        public MigrationForm()
        {
            InitializeComponent();
        }

        private void MigrationForm_Load(object sender, EventArgs e)
        {
            picasaService = new Google.GData.Photos.PicasaService("BloggerImageConvertion");
            var loginForm = new GoogleLogin(picasaService);
            if (loginForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                picasaService.SetAuthenticationToken(loginForm.token);
                user = loginForm.User;
                LoadGalleries();
                this.picasaService.AsyncOperationCompleted += new AsyncOperationCompletedEventHandler(this.OnDone);
                this.picasaService.AsyncOperationProgress += new AsyncOperationProgressEventHandler(this.OnProgress);
            }
        }

        /// <summary>
        /// Fires when the PicasaService has a progress update
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnProgress(object sender, AsyncOperationProgressEventArgs e)
        {
            this.CancelAsync.Enabled = true;
            this.CancelAsync.Visible = true;

            if (this.states.Contains(e.UserState as UserState) == true)
            {
                var ut = e.UserState as UserState;
                imageMigrationCollection.Where(i => i.Original == ut.filename).ToList().ForEach(i => { i.Progress = e.ProgressPercentage; i.Status = "Uploading"; });
                gridImageMigration.EndUpdate();
            }
        }

        /// <summary>
        /// Fires when the PicasaService is complete
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDone(object sender, AsyncOperationCompletedEventArgs e)
        {
            UserState ut = e.UserState as UserState;

            if (this.states.Contains(ut) == false)
                return;

            this.states.Remove(ut);

            if (e.Error == null && e.Cancelled == false)
            {

                if (ut.opType == UserState.OperationType.query ||
                    ut.opType == UserState.OperationType.queryForBackup)
                {
                    if (e.Feed != null)
                    {
                        this.photoFeed = e.Feed as PicasaFeed;
                    }
                }

                if (ut.opType == UserState.OperationType.upload)
                {
                    if (e.Entry != null)
                    {
                        imageMigrationCollection.Where(i => i.Original == ut.filename).ToList().ForEach(i => { i.Progress = 100; i.New = e.Entry.Content.AbsoluteUri; i.Status = "Complete"; });
                        if (totalProgress.Value < totalProgress.Maximum)
                        {
                            totalProgress.Value += 1;
                        }

                        if (IsMigrationComplete())
                        {
                            ReplaceImagesAndSave();
                        }
                    }
                }
            }

            if (this.states.Count == 0)
            {
                this.CancelAsync.Enabled = false;
                this.CancelAsync.Visible = false;
            }

        }

        /// <summary>
        /// Loads the album information
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="albumTitle"></param>
        public void StartQuery(string uri, string albumTitle)
        {
            UserState us = new UserState();
            this.states.Add(us);

            us.opType = UserState.OperationType.query;
            us.filename = albumTitle;

            this.picasaService.QueryFeedAync(new Uri(uri), DateTime.MinValue, us);
        }


        /// <summary>
        /// Populates the Gallary drop down
        /// </summary>
        private void LoadGalleries()
        {
            AlbumQuery query = new AlbumQuery();
            query.Uri = new Uri(PicasaQuery.CreatePicasaUri(user));

            feed = picasaService.Query(query);


            cboGallery.Items.Clear();
            foreach (PicasaEntry entry in feed.Entries)
            {
                var item = cboGallery.Items.Add(entry, String.Format("{0} ({1})", entry.Title.Text, entry.GetPhotoExtensionValue(GPhotoNameTable.NumPhotos)));
                item.Tag = entry;
            }
        }

        /// <summary>
        /// Loads the Source XML file and extracts image links
        /// </summary>
        private void RefreshImageUrls()
        {
            var exportFileContents = System.IO.File.ReadAllText(txtSourceFilename.Text);

            try
            {
                var exportFileDataSet = new System.Data.DataSet();

                exportFileDataSet.ReadXml(txtSourceFilename.Text);

                txtDomain.Text = exportFileDataSet.Tables["link"].Rows[0]["href"].ToString().Trim();
            }
            catch (Exception)
            {

            }

            System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"https?:\/\/\S*?\.(?:png|jpg|gif|jpeg)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            var matches = regex.Matches(exportFileContents);

            imageMigrationCollection = new BindingList<ImageMigrationItem>();
            imageMigrationCollection.ListChanged += imageMigrationCollection_ListChanged;

            foreach (System.Text.RegularExpressions.Match match in matches)
            {
                if (!imageMigrationCollection.Any(i => i.Original == match.Value))
                {
                    if ((cbOnlyMigrateHostedImages.Checked && match.Value.ToString().StartsWith(txtDomain.Text)) ||
                        cbOnlyMigrateHostedImages.Checked == false)
                    {
                        imageMigrationCollection.Add(new ImageMigrationItem()
                        {
                            Selected = true,
                            Original = match.Value
                        });
                    }

                }
            }

            gridImageMigration.DataSource = imageMigrationCollection;

        }


        /// <summary>
        /// Checks if all selected images have been migrated
        /// </summary>
        /// <returns></returns>
        private bool IsMigrationComplete()
        {

            return !imageMigrationCollection.Any(i => i.IsComplete == false);
        }

        /// <summary>
        /// Replaces the Source XML with migrated image urls and saves file
        /// </summary>
        private void ReplaceImagesAndSave()
        {
            var fileContents = System.IO.File.ReadAllText(txtSourceFilename.Text);
            imageMigrationCollection.Where(i => i.Selected && String.IsNullOrWhiteSpace(i.New) == false).ToList().ForEach(i =>
            {
                var fullResImageUrl = i.New.Insert(i.New.LastIndexOf("/"), @"/s0");
                fileContents = fileContents.Replace(i.Original, fullResImageUrl);
            });

            if (cbStripCaptionTags.Checked)
            {
                fileContents = System.Text.RegularExpressions.Regex.Replace(fileContents, @"\[caption\s.+?\]", "", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            }
            System.IO.File.WriteAllText(txtDestinationFilename.Text, fileContents);
            InProcess = false;
            MessageBox.Show("Done!");
        }

        private void btnOpenSourceFile_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                var exportFile = openFileDialog.FileName;
                txtSourceFilename.Text = exportFile;
                var fInfo = new System.IO.FileInfo(exportFile);
                txtDestinationFilename.Text = System.IO.Path.Combine(fInfo.DirectoryName, System.IO.Path.GetFileNameWithoutExtension(fInfo.FullName) + "-MigratedImages" + fInfo.Extension);
                RefreshImageUrls();
            }
        }

        void imageMigrationCollection_ListChanged(object sender, ListChangedEventArgs e)
        {
            btnMigrate.Text = String.Format("Migrate {0} Image(s)", imageMigrationCollection.Count(i => i.Selected));
        }

        private void btnMigrate_Click(object sender, EventArgs e)
        {
            if (ultraValidator.Validate("Migrate").IsValid == true)
            {
                if (cboGallery.SelectedIndex >= 0)
                {
                    InProcess = true;
                    totalProgress.Value = 0;
                    totalProgress.Maximum = imageMigrationCollection.Where(i => i.Selected).Count();
                    foreach (var item in imageMigrationCollection)
                    {
                        if (item.Selected)
                        {
                            gridImageMigration.BeginUpdate();
                            string url = item.Original;
                            string extension = url.Split('.').Last().ToLower();
                            item.Status = "Initializing...";

                            UserState ut = new UserState();
                            ut.opType = UserState.OperationType.upload;
                            ut.filename = url;
                            ut.extension = extension;

                            var client = new System.Net.WebClient();
                            client.DownloadDataCompleted += client_DownloadDataCompleted;
                            client.DownloadProgressChanged += client_DownloadProgressChanged;
                            client.DownloadDataAsync(new Uri(url), ut);

                        }
                    }
                }
            }
        }

        void client_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
        {
            var ut = e.UserState as UserState;
            if (ut != null)
            {
                imageMigrationCollection.Where(i => i.Original == ut.filename).ToList().ForEach(i => { i.Progress = e.ProgressPercentage; i.Status = "Downloading"; });
                gridImageMigration.EndUpdate();
            }
        }

        void client_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
        {
            var ut = e.UserState as UserState;
            if (ut != null)
            {
                if (e.Error != null)
                {
                    imageMigrationCollection.Where(i => i.Original == ut.filename).ToList().ForEach(i => { i.Progress = 0; i.Status = String.Format("Error: {0}", e.Error.Message) ; });
                    gridImageMigration.EndUpdate();
                    if (totalProgress.Value < totalProgress.Maximum)
                    {
                        totalProgress.Value += 1;
                    }
                }
                else
                {
                    imageMigrationCollection.Where(i => i.Original == ut.filename).ToList().ForEach(i => { i.Progress = 0; i.Status = "Uploading"; });
                    gridImageMigration.EndUpdate();

                    PicasaEntry entry = new PhotoEntry();
                    this.states.Add(ut);
                    System.IO.Stream stream = new System.IO.MemoryStream((byte[])e.Result);
                    entry.MediaSource = new Google.GData.Client.MediaFileSource(stream, ut.filename, "image/" + ut.extension);
                    this.picasaService.InsertAsync(new Uri(this.photoFeed.Post), entry, ut);
                }

            }

        }

        private void cboGallery_ValueChanged(object sender, EventArgs e)
        {
            PicasaEntry entry = cboGallery.SelectedItem.Tag as PicasaEntry;
            string photoUri = entry.FeedUri;
            if (photoUri != null)
            {
                StartQuery(photoUri, entry.Title.Text);
            }
        }

        private void cbOnlyMigrateHostedImages_CheckedChanged(object sender, EventArgs e)
        {
            RefreshImageUrls();
        }

        private void ultraToolbarsManager_ToolClick(object sender, Infragistics.Win.UltraWinToolbars.ToolClickEventArgs e)
        {
            switch (e.Tool.Key)
            {
                case "btnUncheckSelected":
                    foreach (var row in gridImageMigration.Selected.Rows)
                    {
                        ((ImageMigrationItem)row.ListObject).Selected = false;
                    }
                    break;
                case "btnCheckSelected":
                    foreach (var row in gridImageMigration.Selected.Rows)
                    {
                        ((ImageMigrationItem)row.ListObject).Selected = true;
                    }
                    break;
                case "btnUncheckAll":
                    imageMigrationCollection.ToList().ForEach(i => i.Selected = false);
                    break;
                case "btnCheckAll":
                    imageMigrationCollection.ToList().ForEach(i => i.Selected = true);

                    break;

            }
        }

        private void btnOpenDestinationFile_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrWhiteSpace(txtDestinationFilename.Text))
            {
                saveFileDialog.FileName = txtDestinationFilename.Text;
            }
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtDestinationFilename.Text = saveFileDialog.FileName;
            }
        }

        private void cboGallery_EditorButtonClick(object sender, Infragistics.Win.UltraWinEditors.EditorButtonEventArgs e)
        {
            switch (e.Button.Key)
            {
                case "Create":
                    NewAlbumDialog dialog = new NewAlbumDialog(this.picasaService, this.feed);
                    dialog.ShowDialog();
                    PicasaEntry entry = dialog.CreatedEntry;
                    if (entry != null)
                    {
                        LoadGalleries();
                    }
                    break;
            }
        }

        private void gridImageMigration_BeforeCellUpdate(object sender, Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs e)
        {
            e.Cancel = InProcess;

        }

        private void CancelAsync_Click(object sender, EventArgs e)
        {
            foreach (object o in this.states)
            {
                this.picasaService.CancelAsync(o);
            }
            InProcess = false;
        }
    }


    public class UserState
    {
        public enum OperationType
        {
            upload,
            download,
            downloadList,
            query,
            queryForBackup
        }

        public string filename;
        public OperationType opType;
        public PicasaFeed feed;
        public int counter = 0;
        public string foldername;
        public string extension;

    }


}
